home *** CD-ROM | disk | FTP | other *** search
- // **********************************************
- // File: EDIT.CPP
- // Edit window input module
-
- #include "muzika.h"
- #include <stdlib.h>
- #include <values.h>
-
- // **********************************************
- // InsertEmptyStaff inserts a new empty staff in the specified list
- // given the staff Y location and the part multiplicity.
- // The list is kept sorted by the Y locations in ascending order.
-
- void InsertEmptyStaff(IndexedList &staffList, unsigned Y, int multiplicity)
- {
- // Find the index to insert at
- for (int index = 0;
- index < staffList.number() && ((Staff *) &staffList[index])->Y() < Y;
- index += multiplicity);
- if (index > staffList.number())
- index = staffList.number();
-
- // Update coordinates of other staves and of the marked block
- for (int other = index; other < staffList.number(); ++other)
- ((Staff *) &staffList[other])->Y() += pixelsPerStaff;
- if (markBeginStaff >= index)
- ++markBeginStaff;
- if (markEndStaff >= index)
- ++markEndStaff;
-
- // Adjust Y to a multiple of pixelsPerStaff
- if (index)
- Y = ((Staff *) &staffList[index-1])->Y()+pixelsPerStaff;
- else
- Y = pixelsPerStaff;
-
- // Insert the staff
- staffList.insertAt(*new Staff(Y), index);
- }
-
- // **********************************************
- // NewMultipleStaff creates a new multiple staff, after the user
- // has clicked the pencil-on-staff symbol.
- // The staff is inserted in the list, and the scroll bar range
- // is readjusted.
-
- void NewMultipleStaff(int Y)
- {
- Part &p = *((Part *) &melody.part[displayedPart]);
-
- // Insert a new staff group in the database and refresh screen
- for (int i = 0; i < p.multiplicity(); ++i)
- InsertEmptyStaff(p.staff, Y+p.GetPartY(), p.multiplicity());
- melodyModified = TRUE;
- SetScrollRange(hEditWnd, SB_VERT, 0,
- ((Staff *) &p.staff[p.staff.number()-1])->Y(), TRUE);
- InvalidateRect(hEditWnd, NULL, TRUE);
- }
-
- // **********************************************
- // IdentifyStaff finds the index of the staff to which the Y coordinate
- // (obtained from the current cursor position) is closest.
-
- int IdentifyStaff(IndexedList &staffList, unsigned Y)
- {
- unsigned minDistance = MAXINT;
- int minIndex;
- int staffY;
-
- // Find a staff that is closest to the clicked point,
- // by minimizing the distance from different staves
- for (int index = 0; index < staffList.number(); ++index) {
- // If point actually on the staff, return it
- if ((staffY = ((Staff *) &staffList[index])->Y()) <= Y &&
- staffY+24 >= Y)
- return index;
- int distance = (staffY > Y) ? staffY-Y : Y-staffY-25;
- if (distance < minDistance) {
- minDistance = distance;
- minIndex = index;
- }
- }
-
- // Return a staff only if within reasonable distance
- return (minDistance < (pixelsPerStaff-25)/2) ? minIndex : -1;
- }
-
- // **********************************************
- // DeleteMultipleStaff deletes the multiple staff identified
- // by a Y coordinate (obtained from the current cursor position).
- // If the staff contains any objects, the user is requested
- // to confirm the operation.
-
- void DeleteMultipleStaff(int Y)
- {
- // Obtain a pointer to the staff to delete
- Part &p = *((Part *) &melody.part[displayedPart]);
- int staffIndex = IdentifyStaff(p.staff, (Y += p.GetPartY()));
- if (staffIndex < 0) return;
- staffIndex = staffIndex/p.multiplicity()*p.multiplicity();
- Staff *s = (Staff *) &p.staff[staffIndex];
-
- // Check if the multiple staff contains any objects
- for (int i = staffIndex;
- i/p.multiplicity() == staffIndex/p.multiplicity(); ++i) {
- Staff *s = (Staff *) &p.staff[i];
- if (s->pointObject.number() || s->continuousObject.number())
- if (MessageBox(hEditWnd, "Staff is not empty. Erase anyway?", "WARNING",
- MB_ICONEXCLAMATION | MB_YESNOCANCEL) == IDYES)
- break;
- else return;
- }
-
- // Erase the staff
- int staffY = s->Y();
- for (i = 0; i < p.multiplicity(); ++i)
- p.staff.destroyAt(staffIndex);
- if (markBeginStaff > staffIndex)
- markBeginStaff -= p.multiplicity();
- if (markEndStaff >= staffIndex)
- markEndStaff -= p.multiplicity();
- if (markEndStaff < markBeginStaff)
- UnmarkBlock();
- if (p.staff.number() > staffIndex) {
- int staffDiff = ((Staff *) &p.staff[staffIndex])->Y()-staffY;
- for (; staffIndex < p.staff.number(); ++staffIndex)
- ((Staff *) &p.staff[staffIndex])->Y() -= staffDiff;
- }
-
- // Mark the melody as modified and readjust the scroll bar range
- melodyModified = TRUE;
- SetScrollRange(hEditWnd, SB_VERT, 0,
- p.staff.number() ? ((Staff *) &p.staff[p.staff.number()-1])->Y() : 0, TRUE);
- InvalidateRect(hEditWnd, NULL, TRUE);
- }
-
- // **********************************************
- // NewPointObject creates a point object corresponding to the
- // active symbol at the given coordinate (obtained from the current
- // cursor position), and inserts it in the staff according
- // to the object _location attribute.
-
- void NewPointObject(SymbolClass *symbol, int X, int Y)
- {
- // Obtain the staff to insert the object into
- Part &p = *((Part *) &melody.part[displayedPart]);
- int staffIndex = IdentifyStaff(p.staff, (Y += p.GetPartY()));
- Staff *s = (staffIndex >= 0) ? (Staff *) &p.staff[staffIndex] : NULL;
- if (!s || X < s->X() || X >= s->X()+s->width()) return;
-
- // Create the object and decide where to insert it
- int first, last;
- MusicalObject *obj = symbol->CreateObject(staffIndex, WidthRound(X-s->X()), Y -= s->Y());
- if (obj) {
- X = ((PointObject *) obj)->X();
-
- // Check the object _location attribute
- switch (obj->location() & ~ONEPERSTAFF) {
- case INSTAFF:
- case ABOVESTAFF:
- case BELOWSTAFF:
- first = last = staffIndex;
- break;
-
- case ABOVEMULTIPLE:
- first = last = staffIndex/p.multiplicity()*p.multiplicity();
- break;
-
- case BELOWMULTIPLE:
- first = last = (staffIndex/p.multiplicity()+1)*p.multiplicity()-1;
- break;
-
- case COMMONMULTIPLE:
- first = staffIndex/p.multiplicity()*p.multiplicity();
- last = (staffIndex/p.multiplicity()+1)*p.multiplicity()-1;
- break;
- }
-
- // Insert the objects in the required staff or staves
- for (staffIndex = first; staffIndex <= last; ++staffIndex) {
- s = (Staff *) &p.staff[staffIndex];
- IndexedList &list = s->pointObject;
- PointObject *obj1;
- for (int index = 0;
- index < list.number() && (obj1 = (PointObject *) &list[index])->X() <= X;
- ++index)
- if (obj1->location() & ONEPERSTAFF && obj1->X() == X) {
- MessageBox(hEditWnd, "Only one such symbol is allowed per staff", NULL,
- MB_ICONEXCLAMATION | MB_OK);
- return;
- }
- list.insertAt(
- (staffIndex == first) ? *obj : *symbol->CreateObject(staffIndex, X, Y), index);
- }
- }
- melodyModified = TRUE;
-
- // Refresh screen
- InvalidateRect(hEditWnd, NULL, !obj);
- }
-
- // **********************************************
- // NewContinuousObject creates a continuous object corresponding to the
- // active symbol at the given coordinate (obtained from the current
- // cursor position), and inserts it in the staff according
- // to the object _location attribute.
-
- void NewContinuousObject(SymbolClass *symbol, int Xleft, int Xright, int Y)
- {
- // Obtain the staff to insert the object into
- Part &p = *((Part *) &melody.part[displayedPart]);
- int staffIndex = IdentifyStaff(p.staff, (Y += p.GetPartY()));
- Staff *s = (staffIndex >= 0) ? (Staff *) &p.staff[staffIndex] : NULL;
- if (!s || Xleft < s->X() || Xright >= s->X()+s->width()) return;
-
- // Create the object and decide where to insert it
- int first, last;
- MusicalObject *obj =
- symbol->CreateObject(staffIndex, WidthRound(Xleft-s->X()), WidthRound(Xright-s->X()));
- if (obj) {
- Xleft = ((ContinuousObject *) obj)->Xleft();
- Xright = ((ContinuousObject *) obj)->Xright();
-
- // Check the object _location attribute
- switch (obj->location() & ~ONEPERSTAFF) {
- case INSTAFF:
- case ABOVESTAFF:
- case BELOWSTAFF:
- first = last = staffIndex;
- break;
-
- case ABOVEMULTIPLE:
- first = last = staffIndex/p.multiplicity()*p.multiplicity();
- break;
-
- case BELOWMULTIPLE:
- first = last = (staffIndex/p.multiplicity()+1)*p.multiplicity()-1;
- break;
-
- case COMMONMULTIPLE:
- first = staffIndex/p.multiplicity()*p.multiplicity();
- last = (staffIndex/p.multiplicity()+1)*p.multiplicity()-1;
- break;
- }
-
- // Insert the object in the appropriate staff or staves
- for (staffIndex = first; staffIndex <= last; ++staffIndex) {
- s = (Staff *) &p.staff[staffIndex];
- IndexedList &list = s->continuousObject;
- ContinuousObject *obj1;
- for (int index = 0;
- index < list.number() &&
- (obj1 = (ContinuousObject *) &list[index])->Xleft() <= Xleft;
- ++index)
- if (obj1->location() & ONEPERSTAFF && obj1->Xleft() == Xleft) {
- MessageBox(hEditWnd, "Only one such symbol is allowed per staff", NULL,
- MB_ICONEXCLAMATION | MB_OK);
- return;
- }
- list.insertAt((staffIndex == first) ? *obj :
- *symbol->CreateObject(staffIndex, Xleft, Xright), index);
- }
- }
- melodyModified = TRUE;
-
- // Refresh screen
- InvalidateRect(hEditWnd, NULL, !obj);
- }
-
- // **********************************************
- // DeleteMusicalObject deletes the objects that are within
- // (pixelsPerObject/2) pixels away from the given coordinate
- // (obtained from the current cursor position).
-
- void DeleteMusicalObject(int X, int Y)
- {
- // Obtain the staff to delete the objects from
- Part &p = *((Part *) &melody.part[displayedPart]);
- int staffIndex = IdentifyStaff(p.staff, (Y += p.GetPartY()));
- Staff *s = (staffIndex >= 0) ? (Staff *) &p.staff[staffIndex] : NULL;
- if (!s || X < s->X() || X >= s->X()+s->width()) return;
-
- // Scan the point objects list and delete any objects in range
- X -= s->X();
- Y -= s->Y();
- for (int index = 0; index < s->pointObject.number(); ++index) {
- PointObject *obj = (PointObject *) &s->pointObject[index];
- if (abs(obj->X()-X) < pixelsPerObject/2)
-
- // Check the object _location attribute to see if
- // any special treatment is required
- switch (obj->location() & ~ONEPERSTAFF) {
- case ABOVESTAFF:
- case ABOVEMULTIPLE:
- if (Y < 0) {
- s->pointObject.destroyAt(index);
- --index;
- }
- break;
-
- case BELOWSTAFF:
- case BELOWMULTIPLE:
- if (Y > 24) {
- s->pointObject.destroyAt(index);
- --index;
- }
- break;
-
- case INSTAFF:
- s->pointObject.destroyAt(index);
- --index;
- break;
- }
- }
-
- // Scan the continuous objects list and delete any objects in range
- for (index = 0; index < s->continuousObject.number(); ++index) {
- ContinuousObject *obj = (ContinuousObject *) &s->continuousObject[index];
- if (abs(obj->Xleft()-X) < pixelsPerObject/2)
- switch (obj->location() & ~ONEPERSTAFF) {
- case ABOVESTAFF:
- case ABOVEMULTIPLE:
- if (Y < 0) {
- s->continuousObject.destroyAt(index);
- --index;
- melodyModified = TRUE;
- }
- break;
-
- case BELOWSTAFF:
- case BELOWMULTIPLE:
- if (Y > 24) {
- s->continuousObject.destroyAt(index);
- --index;
- melodyModified = TRUE;
- }
- break;
-
- case INSTAFF:
- s->continuousObject.destroyAt(index);
- --index;
- melodyModified = TRUE;
- break;
- }
- }
-
- // Scan all staves in group and delete COMMONMULTIPLE objects
- int first = staffIndex/p.multiplicity()*p.multiplicity();
- int last = (staffIndex/p.multiplicity()+1)*p.multiplicity()-1;
- for (staffIndex = first; staffIndex <= last; ++staffIndex) {
- // Delete COMMONMULTIPLE point objects
- s = (Staff *) &p.staff[staffIndex];
- for (index = 0; index < s->pointObject.number(); ++index) {
- PointObject *obj = (PointObject *) &s->pointObject[index];
- if (abs(obj->X()-X) < pixelsPerObject/2 &&
- (obj->location() & ~ONEPERSTAFF) == COMMONMULTIPLE) {
- s->pointObject.destroyAt(index);
- --index;
- melodyModified = TRUE;
- }
- }
- for (index = 0; index < s->continuousObject.number(); ++index) {
- // Delete COMMONMULTIPLE continuous objects
- ContinuousObject *obj = (ContinuousObject *) &s->continuousObject[index];
- if (abs(obj->Xleft()-X) < pixelsPerObject/2 &&
- (obj->location() & ~ONEPERSTAFF) == COMMONMULTIPLE) {
- s->continuousObject.destroyAt(index);
- --index;
- melodyModified = TRUE;
- }
- }
- }
-
- // Refresh screen
- InvalidateRect(hEditWnd, NULL, TRUE);
- }
-
- // **********************************************
- // MoveStaff moves a staff from Yfrom to Yto, both coordinates
- // obtained from the mouse cursor. Before actually moving,
- // the destination is checked to be free from other staves.
-
- void MoveStaff(int Yfrom, int Yto)
- {
- // Obtain a pointer to the staff to move
- Part &p = *((Part *) &melody.part[displayedPart]);
- int staffIndex = IdentifyStaff(p.staff, (Yfrom += p.GetPartY()));
- if (staffIndex < 0) return;
- staffIndex = staffIndex/p.multiplicity()*p.multiplicity();
- Staff *s = (Staff *) &p.staff[staffIndex];
- int lastMoved = staffIndex+p.multiplicity()-1;
-
- // Verify that the destination is not occupied
- Yto += p.GetPartY();
- for (int indexTo = 0; indexTo < p.staff.number(); indexTo += p.multiplicity())
- if (indexTo != staffIndex) {
- Staff &firstTo = *((Staff *) &p.staff[indexTo]);
- Staff &lastTo = *((Staff *) &p.staff[indexTo+p.multiplicity()-1]);
- if (Yto <= lastTo.Y()+24 &&
- ((Staff *) &p.staff[lastMoved])->Y()-s->Y()+Yto+24 >= firstTo.Y()) {
- // The destination is occupied:
- // display an error message
- MessageBox(hEditWnd, "Cannot move onto another staff", NULL,
- MB_ICONEXCLAMATION | MB_OK);
- return;
- }
- }
-
- // Set the staff new coordinates and move it
- int distance = Yto-s->Y();
- for (; staffIndex <= lastMoved; ++staffIndex)
- ((Staff *) &p.staff[staffIndex])->Y() += distance;
- staffIndex -= p.multiplicity();
- for (indexTo = 0;
- indexTo < p.staff.number() && ((Staff *) &p.staff[indexTo])->Y() < Yto;
- indexTo += p.multiplicity());
- while (staffIndex <= lastMoved) {
- s = (Staff *) &p.staff[staffIndex];
-
- // Detach and re-insert staves whose index is between
- // the old and new indexes of the moved staff
- p.staff.detachAt(staffIndex);
- if (indexTo > staffIndex) {
- --lastMoved;
- --indexTo;
- }
- p.staff.insertAt(*s, indexTo);
- if (staffIndex >= indexTo) {
- ++staffIndex;
- ++indexTo;
- }
- }
-
- // Refresh the display
- UnmarkBlock();
- melodyModified = TRUE;
- InvalidateRect(hEditWnd, NULL, TRUE);
- SetScrollRange(hEditWnd, SB_VERT, 0,
- ((Staff *) &p.staff[p.staff.number()-1])->Y(), TRUE);
- }
-
- // **********************************************
- // MoveMusicalObject moves a musical object by using
- // the CutBlock and PasteBlock functions in BLOCK.CPP.
-
- void MoveMusicalObject(int Xfrom, int Yfrom, int Xto, int Yto)
- {
- // Verify both points are on the same staff
- Part &p = *((Part *) &melody.part[displayedPart]);
- int staffFrom = IdentifyStaff(p.staff, Yfrom+p.GetPartY());
- int staffTo = IdentifyStaff(p.staff, Yto+p.GetPartY());
- if (staffFrom < 0 || staffTo < 0)
- return;
- staffFrom = staffFrom/p.multiplicity()*p.multiplicity();
- staffTo = staffTo/p.multiplicity()*p.multiplicity();
- int staffX = ((Staff *) &p.staff[staffFrom])->X();
-
- // Use the cut and paste functions to move the objects
- MarkBlock(staffFrom, WidthRound(Xfrom-staffX),
- staffFrom, WidthRound(Xfrom-staffX));
- CutBlock();
- PasteBlock(staffTo, WidthRound(Xto-staffX));
- InvalidateRect(hEditWnd, NULL, TRUE);
- }
-